package hasthi.tools;

import static hasthi.common.constants.WSDMConstants.CAPABILITY_CONFIGURATION;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_DESCRIPTION;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_HEARTBEAT;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_MATRICS1;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_METADATA;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_OPERATIONAL_STATUS;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_OPERATIONAL_STATUS2;
import static hasthi.common.constants.WSDMConstants.CAPABILITY_STATE;
import hasthi.common.BaseContext;
import hasthi.common.ComponetFactory;
import hasthi.common.HasthiException;
import hasthi.common.HasthiOptions;
import hasthi.common.ManagementNotifier;
import hasthi.common.Utils;
import hasthi.common.XmlBeanUtils;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.common.constants.WSDMConstants;
import hasthi.common.constants.WSDMConstants.SystemState;
import hasthi.container.CommunicationContianier;
import hasthi.container.MessageContext;
import hasthi.container.MessageProcessor;
import hasthi.container.UnicastContianier;
import hasthi.container.XsulBasedWSDMProcessor;
import hasthi.instrument.JavaInstrumentation;
import hasthi.metamodel.resource.ManagedResource;
import hasthi.wsdm.AbstractManagedSystemHandle;
import hasthi.wsdm.ManagementAgentContext;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.xml.namespace.QName;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.xmlbeans.XmlObject;

public class HasthiAppender extends AppenderSkeleton implements MessageProcessor{
    private int fatalEventCount = 0;
    private int errorCount = 0;
    private int warnningCount = 0;
    
    private String serviceName;
    private String bootstrapnode;
    
    private XsulBasedWSDMProcessor processor;
    private UnicastContianier container;
    

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getBootstrapnode() {
        return bootstrapnode;
    }

    public void setBootstrapnode(String bootstrapnode) {
        this.bootstrapnode = bootstrapnode;
    }

    @Override
    protected void append(LoggingEvent event) {
        if(Level.FATAL == event.getLevel()){
            fatalEventCount++;
        }else if(Level.ERROR == event.getLevel()){
            errorCount++;
        }else if(Level.WARN == event.getLevel()){
            warnningCount++;
        }
    }

    @Override
    public void close() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    @Override
    public void activateOptions() {
        if(serviceName == null){
            System.err.println("Can not find serviceName, not initializing Hasthi appender");
            return;
        }
        if(bootstrapnode == null){
            System.err.println("Can not find bootstrapnode, not initializing Hasthi appender");
            return;
        }
        try {
            URL url = new URL(bootstrapnode);
            Properties properties = new Properties();
            properties.setProperty(HasthiOptions.BOOTSTRAP_NODE, url.getHost());
            properties.setProperty(HasthiOptions.BOOTSTRAP_SOAP_PORT, String.valueOf(url.getPort()));
            final BaseContext baseContext = new BaseContext(properties);
            ManagementAgentContext context = new ManagementAgentContext(baseContext);
            
            container = ComponetFactory.createSoapContianier(baseContext, this,6007,false);
            container.startContainer();
            processor = new XsulBasedWSDMProcessor(context);
            processor.addResource(new ServiceProxy(new QName(HasthiConstants.MANEGER_NS+"/"+serviceName,serviceName)));

            
            // TODO Auto-generated method stub
            super.activateOptions();
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HasthiException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void addContainer(CommunicationContianier container) throws HasthiException {
        // TODO Auto-generated method stub
        
    }

    public XmlObject processMessage(MessageContext messageContext) throws HasthiException {
        if (processor.isKnownMessage(messageContext)) {
            return  processor.processeMessage(messageContext);
        } else {
            System.err.println("Unknown message="+XmlBeanUtils.prettyPrint(messageContext.getRequest()));
            return null;
        }
    }
  

    public class ServiceProxy extends AbstractManagedSystemHandle {
        protected Map<QName, Object> systemProperties = new HashMap<QName, Object>();
    
        public ServiceProxy(QName name) throws HasthiException {
            super(Utils.getTimestampedName(name), name.getLocalPart());
                supportedCapabilityNames.clear();
                supportedCapabilityNames.add(CAPABILITY_DESCRIPTION);
                supportedCapabilityNames.add(CAPABILITY_MATRICS1);
                supportedCapabilityNames.add(CAPABILITY_OPERATIONAL_STATUS);
                supportedCapabilityNames.add(CAPABILITY_OPERATIONAL_STATUS2);
                supportedCapabilityNames.add(CAPABILITY_STATE);
                supportedCapabilityNames.add(CAPABILITY_HEARTBEAT);
                supportedCapabilityNames.add(CAPABILITY_CONFIGURATION);
                supportedCapabilityNames.add(CAPABILITY_METADATA);
                
                supportedMatricNames.add(HasthiConstants.MEMEORY_USAGE);
                supportedMatricNames.add(HasthiConstants.THREAD_COUNT);
    
                systemProperties.put(WSDMConstants.RESOURCE_CATEGORY_PARAM,
                        ManagedResource.ResourceType.Service.toString());
                systemProperties.put(WSDMConstants.RESOURCE_TYPE_PARAM,name.toString());                
                systemProperties.put(WSDMConstants.HOSTNAME_PARAM,Utils.getLocalHost());
        }
    
        public Object getMatric(QName name) throws HasthiException {
            if(HasthiConstants.MEMEORY_USAGE.equals(name)){
                return String.valueOf(JavaInstrumentation.getMemoryUsage());
            }else if(HasthiConstants.THREAD_COUNT.equals(name)){
                return String.valueOf(JavaInstrumentation.getThreadCount());
            }else{
                throw new HasthiException(this.name + " resource configuration define matric "+name+ " but can not find it while polling",FaultCode.ConfigError);
            }
        }
    
        public SystemState getCurrentSystemState() {
         if(fatalEventCount > 0){
             return SystemState.FaultyState;
         }else{
             return SystemState.IdleState;
         }   
        }
    
        public SystemState[] getLastStateTransition() {
            if (fatalEventCount > 0) {
                return new SystemState[] { SystemState.IdleState, SystemState.FaultyState };
            } else {
                return new SystemState[] { SystemState.UpState, SystemState.IdleState };
            }
    
        }
    
        public ManagementNotifier getNotifier() {
            return null;
        }
    
        public String getSystemEndPoint() {
            return container.getContainierAddress() +HasthiConstants.RESOURCE_PREFIX+name.getLocalPart();
        }
    
        public void shutDown() throws HasthiException {
            container.stopContainer();
        }
    
        public void setConfiguration(QName name, Object value) throws HasthiException {
            if (systemProperties.containsKey(name) || supportedConfigurationNames.contains(name)) {
                systemProperties.put(name, value);
            } else {
                throw new HasthiException("Does not support configuration " + name,
                        FaultCode.InvaliedLocalArgumnet);
            }
        }
    
        public Object getConfiguration(QName name) throws HasthiException {
            if(systemProperties.containsKey(name)){
                return systemProperties.get(name);
            }else{
                return null;
            }
        }
    }
}


